package ocumed.presentation.termin;

import ocumed.presentation.arzt.AerzteComboModel;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.table.AbstractTableModel;

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
import ocumed.applikation.exceptions.NoFehlzeitException;
import ocumed.applikation.exceptions.NoOrdZeitenException;
import ocumed.applikation.exceptions.WrongDayException;
import ocumed.applikation.terminverwaltung.TerminVergeben;
import org.jdesktop.swingx.JXTable;

/**
 *
 * @author apr3455
 */
public class TerminKalender extends AbstractTableModel {
    
    //----------TIMESPAN SETTINGS-----------------//
    private int DAY_START = 8;
    private int DAY_END = 19;
    private int HOUR_PARTITION = 6;
    //--------------------------------------------//
    
    protected String[] m_columnNames = null;
    protected String[] m_Dates = null;
    private Boolean[][] m_ordZeiten = null;
    private Object[][] m_data = null;
    private Integer[][] m_terminIds = null;
    private Boolean[][] m_fZeiten = null;
    private String[] m_weekdays = {"so", "mo", "di", "mi", "do", "fr", "sa"}; // TODO update
    private JXTable m_table = null;
    private Date m_date = null;
    private AerzteComboModel m_aerzteCombo = null;
    private HashMap<String, Integer> m_timeRow = new HashMap<String, Integer>();
    protected HashMap<String, Integer> m_dateCol = new HashMap<String, Integer>();
    protected TerminVergeben m_controller = null;
   
    public TerminKalender(JXTable table, AerzteComboModel aerzteModel, TerminVergeben controller){
        m_table = table;
        m_controller = controller;
        m_aerzteCombo = aerzteModel;
        createTimespan();
        buildKalender(new Date());
    }
    
    public TerminKalender(JXTable table, TerminVergeben controller){
        m_table = table;
        m_controller = controller;
        createTimespan();
        buildKalender(new Date());
    }
    
    public TerminKalender(){
        
    }
    
    public void buildKalender(Date d){
        clear();
        m_date = getFirstDayOfWeek(d);
        setColumnHeaders(m_date);
        setOrdinationszeiten();
        setKalenderValues();
        fireTableStructureChanged();
    }

    public void update(){
        clear();
        setColumnHeaders(m_date);
        setOrdinationszeiten();
        setKalenderValues();
        for(int i = 0; i < getRowCount(); i++){
            for(int e = 1; e < getColumnCount(); e++){
                fireTableCellUpdated(i, e);
            }
        }
    }
    
    void clear(){
        int rows = (DAY_END - DAY_START) * HOUR_PARTITION;
        int cols = getHeaderCount();
        m_dateCol.clear();
        if(m_data != null){
            for(Integer i = 0; i < rows; i++){
                for(Integer e = 1; e < cols; e++){
                    m_data[i][e] = "";
                    m_terminIds[i][e] = null;
                    m_ordZeiten[i][e] = null;
                    m_fZeiten[i][e] = null;
                }
            }
        }
    }
    
    void reload() {
        clear();
        setColumnHeaders(m_date);
        setOrdinationszeiten();
        setKalenderValues();
        fireTableStructureChanged();
    }

    void showDate(int days) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(m_date);
        cal.add(Calendar.DAY_OF_WEEK, days);
        m_date = cal.getTime();
        buildKalender(m_date);
    }
    
    void showToday() {
        Calendar cal = Calendar.getInstance();
        cal.setTime(new Date());
        m_date = cal.getTime();
        buildKalender(m_date);
    }
    
    public Date getFirstDayOfWeek(Date d){
        Calendar cal = Calendar.getInstance();
        cal.setFirstDayOfWeek(Calendar.MONDAY);
        cal.setTime(d);
        while(cal.get(Calendar.DAY_OF_WEEK) != Calendar.MONDAY){
            cal.add(Calendar.DAY_OF_WEEK, -1);
        }
        return cal.getTime();
    }
    
    protected void setColumnHeaders(Date date){
        Date d = null;
        m_columnNames = (m_columnNames == null)? new String[getHeaderCount()]: m_columnNames;
        m_Dates = (m_Dates == null)? new String[getHeaderCount()]: m_Dates;
        m_columnNames[0] = "Uhrzeit";
        Calendar cal = Calendar.getInstance();
        cal.setFirstDayOfWeek(Calendar.SUNDAY);
        cal.setTime(date);
        SimpleDateFormat df = new SimpleDateFormat("dd.MM.yy");
        for(int i = 1; i < getHeaderCount(); i++){
            d = cal.getTime();
            m_columnNames[i] = m_weekdays[cal.get(Calendar.DAY_OF_WEEK) - 1].substring(0, 1).toUpperCase() + m_weekdays[cal.get(Calendar.DAY_OF_WEEK) - 1].substring(1, 2) + ", " + df.format(d).toString();
            m_Dates[i] = df.format(d).toString();
            m_dateCol.put(df.format(d).toString(), i);
            cal.add(Calendar.DAY_OF_WEEK, 1);
        }
    }
    
    private String getSelectedDate(int col){
        return m_Dates[col];
    }
    
    private String getSelectedTime(int row){
        return (String) m_data[row][0];
    }
    
    private void createTimespan(){
        int rows = (DAY_END - DAY_START) * HOUR_PARTITION;
        int cols = getHeaderCount();
        Integer currHour = DAY_START - 1;
        Integer currHourPart = 0;
        Integer currPart = HOUR_PARTITION;
        m_data = new String[rows][cols];
        m_terminIds = new Integer[rows][cols];
        m_fZeiten = new Boolean[rows][cols];
        m_ordZeiten = new Boolean[rows][cols];
        for(Integer i = 0; i < rows; i++){
            for(Integer e = 0; e < cols; e++){
                if(e == 0){
                    if(currPart < (HOUR_PARTITION)){
                        currHourPart = (60 / HOUR_PARTITION) * currPart;
                        m_data[i][e] = (currHourPart < 10)? "0" + currHourPart.toString(): currHourPart.toString();
                        currPart++;
                    } else {
                        m_data[i][e] = "00";
                        currHour++;
                        currPart = 1;
                    }
                    m_data[i][e] = (currHour < 10)? "0" + currHour.toString() + ":" + m_data[i][e]: currHour.toString() + ":" + m_data[i][e];
                    m_timeRow.put((String)m_data[i][e], i);
                } else {
                    m_data[i][e] = new String("");
                }
            }
        }
    }
    
    public int getColumnCount() {
        return m_columnNames.length;
    }

    public int getRowCount() {
        return m_data.length;
    }
    
    @Override
    public String getColumnName(int col) {
        return m_columnNames[col];
    }

    public Object getValueAt(int row, int col) {
        return m_data[row][col];
    }

    @Override
    public Class getColumnClass(int c) {
        return getValueAt(0, c).getClass();
    }

    /*
     * Don't need to implement this method unless your table's
     * editable.
     */
    @Override
    public boolean isCellEditable(int row, int col) {
        //Note that the data/cell address is constant,
        //no matter where the cell appears onscreen.
        return false;
    }
    
    @Override
    public void setValueAt(Object value, int row, int col) {
        m_data[row][col] = value;
        fireTableCellUpdated(row, col);
    }

    Date getSelectedDate(int selectedRow, int selectedColumn) {
        if(getValueAt(selectedRow, selectedColumn).equals("") == true &&
            m_ordZeiten[selectedRow] != null &&
            m_fZeiten[selectedRow][selectedColumn] == null){
            
            String date = getSelectedDate(selectedColumn);
            String time = getSelectedTime(selectedRow);
            String dateTime = date + " " + time;
            SimpleDateFormat df = new SimpleDateFormat("dd.MM.yy HH:mm");
            Date d = null;
            
            try {
                d = df.parse(dateTime);
            } catch (ParseException ex) {
                Logger.getLogger(TerminKalender.class.getName()).log(Level.SEVERE, null, ex);
            }
            
            return d;
            
        } else {
            return null;
        }
    }

    public void setSelectedDate(Date d){
        SimpleDateFormat dfDate = new SimpleDateFormat("dd.MM.yy");
        SimpleDateFormat dfTime = new SimpleDateFormat("HH:mm");
        int row = m_timeRow.get(dfTime.format(d).toString());
        int col = m_dateCol.get(dfDate.format(d).toString());
        m_table.changeSelection(row, col, false, false);
    }

    private void setKalenderValues() {
        int rows = (DAY_END - DAY_START) * HOUR_PARTITION;
        HashMap<Integer, String> termine = null;
        Set<Integer> terminSet = null;
        Iterator<Integer> terminIdIt = null;
        Calendar cal = Calendar.getInstance();
        Calendar terminEndeCal = Calendar.getInstance();
        Calendar terminAnfangCal = Calendar.getInstance();
        SimpleDateFormat dfTime = new SimpleDateFormat("HH:mm");
        Date terminAnfang = null;
        Date terminEnde = null;
        Date currTime = null;
        String date = null;
        String currentPatient = null;
        Integer terminId = null;
        Integer currRow = 0;
        
        cal.setTime(m_date);

        for(int i = 1; i < m_columnNames.length; i++){
            
            setFehlzeiten(cal.getTime(), i);
            termine = m_controller.getTermine(getArztId(i), cal.getTime());
            terminSet = termine.keySet();
            terminIdIt = terminSet.iterator();
            
            while(terminIdIt.hasNext()){
                terminId = terminIdIt.next();
                
                try {
                    terminAnfang = dfTime.parse(dfTime.format(m_controller.getTerminAnfang(terminId)));
                    terminEnde = dfTime.parse(dfTime.format(m_controller.getTerminEnde(terminId)));
                } catch(ParseException ex){
                    //nothing
                }
                
                currentPatient = termine.get(terminId);
                for(int e = 0; e < rows; e++){
                    try {
                        date = (String) getValueAt(e,0);
                        currTime = dfTime.parse(date);
                        if(terminAnfang.equals(currTime)){
                            m_data[e][i] = currentPatient;
                            m_terminIds[e][i] = terminId;
                            terminEndeCal.setTime(terminEnde);
                            terminAnfangCal.setTime(terminAnfang);
                            terminAnfangCal.add(Calendar.MINUTE, (60 / HOUR_PARTITION));
                            currRow = e+1;
                            while(terminEndeCal.after(terminAnfangCal) == true && currRow < rows){
                                m_data[currRow][i] = " ";
                                m_terminIds[currRow][i] = terminId;
                                terminAnfangCal.add(Calendar.MINUTE, (60 / HOUR_PARTITION));
                                currRow++;
                            }
                        }
                    } catch (ParseException ex) {
                        Logger.getLogger(TerminKalender.class.getName()).log(Level.SEVERE, null, ex);
                    }
                   
                }
                
            }
            
            setNextDay(cal);
        }
    }
    
    public Boolean terminDauerMoeglich(Date startDate, Date endDate){
        SimpleDateFormat dfDate = new SimpleDateFormat("dd.MM.yy");
        SimpleDateFormat dfTime = new SimpleDateFormat("HH:mm");
  
        Calendar calStart = Calendar.getInstance();
        Calendar calEnd = Calendar.getInstance();

        calStart.setTime(startDate);
        calEnd.setTime(endDate);
        int col = m_dateCol.get(dfDate.format(startDate).toString());
        int row = 0;
            
        while (calEnd.after(calStart) == true) {
            row = m_timeRow.get(dfTime.format(calStart.getTime()));
            calStart.add(Calendar.MINUTE, 60 / HOUR_PARTITION);
            if(!getValueAt(row, col).equals("") || ordinationsZeit(row, col) == false){
                return false;
            }
        }
        return true;
    }

    private void setOrdinationszeiten() {
        Calendar cal = Calendar.getInstance();
        Calendar calStart = Calendar.getInstance();
        Calendar calEnd = Calendar.getInstance();
        HashMap<Date, Date> ordinationsZeiten = null;
        Set<Date> startSet = null;
        Iterator<Date> it = null;
        SimpleDateFormat dfTime = new SimpleDateFormat("HH:mm");
        Date startDate = null;
        Date endDate = null;
        int row = 0;
        cal.setTime(m_date);
        
        for(int i = 1; i < m_columnNames.length; i++){
            try {
                ordinationsZeiten = m_controller.getOrdinationszeiten(getArztId(i), m_weekdays[cal.get(Calendar.DAY_OF_WEEK) - 1]);
                if(ordinationsZeiten != null){
                    startSet = ordinationsZeiten.keySet();
                    it = startSet.iterator();
                    while(it.hasNext()){
                        startDate = it.next();
                        endDate = ordinationsZeiten.get(startDate);
                        calStart.setTime(startDate);
                        calEnd.setTime(endDate);
                        
                        //----------------Falsche Ordinationszeiten WORKAROUND--------------------//
                        if(calStart.equals(calEnd) == true){
                            calEnd.add(Calendar.HOUR, 23);
                            calEnd.add(Calendar.MINUTE, 50);
                        }
                        //--------------------------------------------------------------//
                        
                        while (calEnd.after(calStart) == true) {
                            if(m_timeRow.containsKey(dfTime.format(calStart.getTime()))){
                                row = m_timeRow.get(dfTime.format(calStart.getTime()));
                                m_ordZeiten[row][i] = true;
                            }
                            calStart.add(Calendar.MINUTE, 60 / HOUR_PARTITION);
                        }
                    }
                }
                setNextDay(cal);
            } catch (WrongDayException e) {
                // nothing to do
                e.printStackTrace();
            } catch (NoOrdZeitenException e) {
                // nothing to do
                e.printStackTrace();    
            }
        }
    }
    
    private void setFehlzeiten(Date d, int column) {
        int rows = (DAY_END - DAY_START) * HOUR_PARTITION;
        Calendar calStart = Calendar.getInstance();
        Calendar calEnd = Calendar.getInstance();
        HashMap<Date, Date> fehlZeiten = null;
	try {
            fehlZeiten = m_controller.getFehlzeiten(getArztId(column), d);
            if(fehlZeiten != null){
                Set<Date> startSet = fehlZeiten.keySet();
                Iterator<Date> it = startSet.iterator();
                SimpleDateFormat dfDate = new SimpleDateFormat("dd.MM.yy");
                SimpleDateFormat dfTime = new SimpleDateFormat("HH:mm");
                Date startDate = null;
                Date endDate = null;
                int row = 0;

                for(int i = 0; i < rows; i++){
                    m_fZeiten[i][column] = null;
                }

                if(it.hasNext()){
                    startDate = it.next();
                    endDate = fehlZeiten.get(startDate);
                    calStart.setTime(startDate);
                    calEnd.setTime(endDate);
                    while (calEnd.after(calStart) == true) {
                        if(m_timeRow.containsKey(dfTime.format(calStart.getTime())) && m_dateCol.containsKey(dfDate.format(calStart.getTime()))){
                           row = m_timeRow.get(dfTime.format(calStart.getTime()));
                           m_fZeiten[row][column] = true;
                        }
                        calStart.add(Calendar.MINUTE, 60 / HOUR_PARTITION);
                    }
                }
            }
	} catch (NoFehlzeitException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
	}
    }
 
    public Integer getTerminId(int row, int col){
        if(row >= 0 && row < getRowCount() && col >= 0 && col < getColumnCount()){
            return m_terminIds[row][col];
        } else {
            return null;
        }
    }
    
    public Date getStartDate(){
        return m_date;
    }
    
    public Boolean ordinationsZeit(int row, int col){
        if(m_ordZeiten[row][col] != null){
            return true;
        } else {
            return false;
        }
    }
    
    public Boolean fehlZeit(int row, int col){
        if(m_fZeiten[row][col] != null){
            return true;
        } else {
            return false;
        }
    }
    
    /*----------------ADAPTER CUNKTIONEN---------------------------*/
    public void setNextDay(Calendar cal){
        //Implementet in Adapter
    }
    
    public int getArztId(int col){
        //Implementet in Adapter
        return m_aerzteCombo.getSelectedId();
    }
    public int getHeaderCount(){
        //Implementet in Adapter
        return 0;
    }
    /*----------------ADAPTER CUNKTIONEN---------------------------*/
}
